package com.xiashitech.agent.instrumentation.base.context;

import java.util.*;
import java.util.function.BiConsumer;

/**
 * TraceContextVars实现
 * @author zhaoyk
 */
public class TraceContextVarsImpl implements TraceContextVars {

    private List<ContextVarName> names;

    private List<ContextVarEntry> entries;

    private int version;

    public TraceContextVarsImpl(int initVersion) {
        names = new ArrayList<>();
        entries = new ArrayList<>();
        version = initVersion;
    }

    @Override
    public String get(String name) {
        ContextVarEntry entry = getEntry(name);
        return entry != null ? entry.getValue() : null;
    }

    private int getIndex(String name) {
        for (int i = 0; i < names.size(); i++) {
            if(names.get(i).getName().equals(name))
                return i;
        }
        return  -1;
    }

    @Override
    public ContextVarEntry getEntry(String name) {
        int index = getIndex(name);
        if(index >= 0)
            return entries.get(index);
        return null;
    }

    @Override
    public TraceContextVars set(String name, String value) {
        return set(new ContextVarName(name), value, null);
    }

    @Override
    public TraceContextVars set(String name, String value, String metadata) {
        return set(new ContextVarName(name), value, metadata);
    }

    @Override
    public TraceContextVars set(ContextVarName varName, String value) {
        return set(varName, value, null);
    }

    @Override
    public TraceContextVars set(ContextVarName varName, String value, String metadata) {
        boolean changed = true;

        ContextVarEntry entry = getEntry(varName.getName());
        if(entry != null) {
            if(!Objects.equals(value, entry.getValue())
                    || (metadata != null && !Objects.equals(metadata, entry.getMetadata()))) {
                entry.setValue(value);

                if(metadata != null)
                    entry.setMetadata(metadata);
            } else {
                changed = false;
            }
        } else {
            names.add(varName);
            entries.add(new ContextVarEntry(value, metadata));
        }

        if(changed)
            updateVersion();

        return this;
    }

    @Override
    public ContextVarName getVarName(String name) {
        int index = getIndex(name);
        if(index >= 0)
            return names.get(index);

        return null;
    }

    @Override
    public boolean remove(String name) {
        int index = getIndex(name);
        if(index >= 0) {
            names.remove(index);
            entries.remove(index);
            updateVersion();
            return true;
        }
        return false;
    }

    @Override
    public int getVersion() {
        return version;
    }

    private void updateVersion() {
        version++;
    }

    @Override
    public int size() {
        return names.size();
    }


    @Override
    public void forEach(BiConsumer<ContextVarName, ContextVarEntry> consumer) {
        for (int i = 0; i < names.size(); i++) {
            consumer.accept(names.get(i), entries.get(i));
        }
    }

}
